package translation;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class ManyToOne implements RuleTypeConstants{
	private Map<String,String> fromXPath;
	private Map<String,String> fromRegEx;
	private Map<String, List<VarBinding>> fromBindings;
	private String toXPath;
	private String toRegEx;

	public ManyToOne(String toXPath, String toRegEx) {
		this.toXPath = toXPath;
		this.toRegEx = toRegEx;
		fromXPath = new LinkedHashMap<String, String>();
		fromRegEx = new LinkedHashMap<String, String>();
		fromBindings = new LinkedHashMap<String, List<VarBinding>>();
	}

	public String getToXPath() {
		return toXPath;
	}

	public void setToXPath(String toXPath) {
		this.toXPath = toXPath;
	}

	public String getToRegEx() {
		return toRegEx;
	}

	public void setToRegEx(String toRegEx) {
		this.toRegEx = toRegEx;
	}

	public Map<String, String> getFromXPath() {
		return fromXPath;
	}	
	public Map<String, String> getFromRegEx() {
		return fromRegEx;
	}
	public void addFromXPath(String fromxPath, String token){
		if(fromXPath == null)
			fromXPath = new LinkedHashMap<String, String>();

		fromXPath.put(fromxPath, token);
	}
	public void addFromRegex(String fromxPath, String fromRegex){
		if(fromRegEx == null)
			fromRegEx = new LinkedHashMap<String, String>();

		fromRegEx.put(fromxPath, fromRegex);
	}
	public void addVarBinding(String fromxPath, VarBinding vb){
		if(fromBindings == null)
			fromBindings = new LinkedHashMap<String, List<VarBinding>>();

		if(fromBindings.get(fromxPath) == null){ //if var binding does not exists for this xpath
			fromBindings.put(fromxPath, new ArrayList<VarBinding>());
		}

		fromBindings.get(fromxPath).add(vb);
	}
	public void removeVarBinding(String fromxPath, int index){
		if(fromBindings != null && fromBindings.get(fromxPath) != null){
			fromBindings.get(fromxPath).remove(index);
		}
	}
	public void removeVarBinding(String fromxPath, VarBinding v){
		if(fromBindings != null && fromBindings.get(fromxPath) != null){
			fromBindings.get(fromxPath).remove(v);
		}
	}	

	public Map<String, List<VarBinding>> getVariableBinding(){
		return fromBindings;
	}	

	/**
	 * Writes the ManyToOne structure to a file
	 * @param output The file to 
	 */
	public void writeToXMLFile(File output){

		BufferedWriter bw = null;
		try {
			//Initialize output file
			if (!output.exists()) {
				output.createNewFile();
			}
			FileWriter fw = new FileWriter(output.getAbsoluteFile(),true); //append to existing file
			bw = new BufferedWriter(fw);

			//write basic tag
			bw.write(ManyToOneStartingTag);

			/* From Fields:
			 * The map fromXPath contains the FromXPath and ??? (used in the Translator)
			 * The map fromRegex contains the fromXPath and fromRegex.
			 * The From field can contain only a fromXpath and thus be SourceFieldType
			 * or can contain everything else, thus be FilteredSourceFieldType
			 * 
			 * The code below basically iterates the fromRegex map and check if variable binding 
			 * exists for the ones that are of FilteredSourceFieldType.
			 * 
			 */

			Iterator<Map.Entry<String, String>> it = fromRegEx.entrySet().iterator();
			while (it.hasNext()) {	
				Map.Entry<String, String> pair = it.next();
				String fromXPath = pair.getKey();
				String fromRegex = pair.getValue();

				//To Regex not exists => SourceFieldType
				if(fromRegex == null && fromBindings.get(fromXPath) == null){
					bw.write(SourceFieldTypeStart);
					bw.write(XPathExpressionStart + fromXPath + XPathExpressionTerminating);
				}
				/*
				 * FilteredSourceFieldType (may contain var binding OR(not XOR) regex)
				 * e.g
				 * <FromField xsi:type="FilteredSourceFieldType">
				   		<XPathExpression>author/dateofbirth/year</XPathExpression>
				   		<VariableBinding>
				   			<ExplicitBinding>$c</ExplicitBinding>
			       		</VariableBinding>
		           </FromField>
				 */
				else{ 
					bw.write(FilteredSourceFieldTypeStart);
					bw.write(XPathExpressionStart + fromXPath + XPathExpressionTerminating);
					if(fromRegex != null)
						bw.write(FilteredWithRegexStart + fromRegex + FilteredWithRegexTerminating);

					if(fromBindings.get(fromXPath) != null){
						for(VarBinding v : fromBindings.get(fromXPath)){
							bw.write(VariableBindingStart);
							if(v.getExplicitPrefixBinding() !=null){
								for(String s : v.getExplicitPrefixBinding()){
									bw.write(ExplicitPrefixBindingStart + s + ExplicitPrefixBindingTerminating);
								}
							}
							else if(v.getListBinding() != null){
								bw.write(ListBindingStart + v.getListBinding() + ListBindingTerminating);
							}
							else if(v.getExplicitPostfixBinding() != null){
								for(String s : v.getExplicitPostfixBinding()){
									bw.write(ExplicitPostfixBindingStart + s + ExplicitPostfixBindingTerminating);
								}
							}
							else if(v.getExplicitBinding() != null){
								bw.write(ExplicitBindingStart + v.getExplicitBinding() + ExplicitBindingTerminating);
							}

							bw.write(VariableBindingTerminating);
						}
					}
				}
				bw.write(TerminatingFromField);
			}

			/*
			 * To Field:
			 * FormattedTargetFieldType FOR SURE as this type combines all from fields 
			 * with the use of token into ONE destination.
			 * 
			 */		

			//To Regex not exists => TargetFieldType
			if(toRegEx == null){
				bw.write(TargetFieldTypeStart);
				bw.write(XPathExpressionStart + toXPath + XPathExpressionTerminating);
			}
			else{
				bw.write(FormattedTargetFieldTypeStart);
				bw.write(XPathExpressionStart + toXPath + XPathExpressionTerminating);
				bw.write(ReplaceWithRegExprStart + toRegEx + ReplaceWithRegExprTerminating);
			}
			bw.write(TargetFieldTypeClosing);

			//Write closing tag
			bw.write(TerminatingTag);

		} catch (IOException e) {
			e.printStackTrace();
		}
		finally{
			try {
				bw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Converts the object to a ManyToOneFieldTranslationType and returns it
	 * @return The converted element
	 */
	public ManyToOneFieldTranslationType toManyToOneFieldTranslationType(){
		ObjectFactory of = new ObjectFactory();		

		//Create object
		ManyToOneFieldTranslationType m2oft = of.createManyToOneFieldTranslationType();

		//From fields
		Iterator<Map.Entry<String, String>> it = fromRegEx.entrySet().iterator();
		while (it.hasNext()) {	
			Map.Entry<String, String> pair = it.next();
			String fromXPath = pair.getKey();
			String fromRegex = pair.getValue();

			//To Regex && Var binding not exists => SourceFieldType
			if(fromRegex == null && fromBindings.get(fromXPath) == null){
				SourceFieldType st = of.createSourceFieldType();
				st.setXPathExpression(fromXPath);
				m2oft.getFromField().add(st);
			}
			else{
				FilteredSourceFieldType fst = of.createFilteredSourceFieldType();
				fst.setXPathExpression(fromXPath);
				fst.setFilterWithRegExpr(fromRegex);
				if(fromBindings.get(fromXPath) != null)
					for(VarBinding v : fromBindings.get(fromXPath)){
						fst.getVariableBinding().add(v.toVariableBinding());
					}	
				m2oft.getFromField().add(fst);
			}
		}
		
		//To field
		if(toRegEx == null){
			TargetFieldType tft = of.createTargetFieldType();
			tft.setXPathExpression(toXPath);
			m2oft.setToField(tft);
		}
		else{
			FormattedTargetFieldType ftft = of.createFormattedTargetFieldType();
			ftft.setXPathExpression(toXPath);
			ftft.setReplaceWithRegExpr(toRegEx);
			m2oft.setToField(ftft);
		}

		return m2oft;
	}		
}